/*
 * TMap Designer - A Thematic Map design software
 * Copyright (C) 2006 World Health Organization
 * http://www.who.int
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */
package who.tmap.ui.util;

import java.awt.Component;
import java.awt.Rectangle;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import javax.swing.JFrame;
import javax.swing.JInternalFrame;

import org.jdom.Attribute;
import org.jdom.DataConversionException;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;

import who.tmap.digitizer.DigitizerService;
import who.tmap.digitizer.impl.DigitizerServiceImpl;
import who.tmap.toolkit.impl.MappingToolKitImpl;
import who.tmap.ui.JFrameAnnotations;
import who.tmap.ui.JFrameData;
import who.tmap.ui.JFrameMain;
import who.tmap.ui.JFrameMap;
import who.tmap.ui.JFrameThematicMap;
import who.tmap.ui.cd.JFrameCartographicDisplay;

public class ConfigFileHelper {

	public static final String MAIN ="main";
	public static final String MAP ="map";
	public static final String ATTRS ="attributes";
	public static final String CD ="cd";
	public static final String ANNOTATIONS ="annotations";
	
	private static final String CONFIG_FILE ="tmapPrefs.xml";
	
    private Element getRootElement( String fileName ) throws JDOMException, IOException {
        SAXBuilder builder = new SAXBuilder();

        builder.setIgnoringElementContentWhitespace(true);
        
        Element rootElt =null;
        
        File file =new File(fileName);
        
        if ( file.exists() ) {        
        	Document doc = builder.build( file );
        
        	rootElt =doc.getRootElement();
        }
        
        return rootElt;
    }	
	
	private FramePreferences loadFrame( Element elt ) throws DataConversionException {
		Rectangle rect =new Rectangle();
			
		rect.setBounds( elt.getAttribute( "x" ).getIntValue(),
						elt.getAttribute( "y" ).getIntValue(),
						elt.getAttribute( "width" ).getIntValue(),
						elt.getAttribute( "height" ).getIntValue() );
		
		FramePreferences framePrefs =new FramePreferences();
		framePrefs.setName( elt.getAttribute( "name" ).getValue() );
		
		framePrefs.setRect( rect );
		Attribute attr =elt.getAttribute( "z" );
		if ( attr != null ) {
			framePrefs.setZOrder( attr.getIntValue() );
		}
		
		framePrefs.setIcon( elt.getAttribute( "icon" ).getBooleanValue() );
		framePrefs.setMaximum( elt.getAttribute( "maximum" ).getBooleanValue() );	
		
		Attribute splitPanePosition =elt.getAttribute( "splitPanePosition" );
		if ( splitPanePosition != null ) {
			framePrefs.setSplitPanePosition( splitPanePosition.getIntValue() );
		} else {
			framePrefs.setSplitPanePosition( -1 );
		}
		
		return framePrefs;
	}    
    
	@SuppressWarnings("unchecked")
	public Map<Integer, FramePreferences> load() throws JDOMException, IOException {
		Map<Integer, FramePreferences> frames =new TreeMap<Integer, FramePreferences>();
		UserHomeHelper userHomeHelper =new UserHomeHelper();
		Element rootElt =getRootElement( userHomeHelper.getTMapDir() + CONFIG_FILE );
		
		if ( rootElt != null ) {	
			Element framesElt =rootElt.getChild( "Frames" );
			
			if ( framesElt != null ) {
				List frameList =framesElt.getChildren( "Frame" );
				
				Iterator it =frameList.iterator();
				while( it.hasNext() ) {
					Element frameElt =(Element)it.next();
					
					FramePreferences prefs =loadFrame( frameElt );
					frames.put( prefs.getZOrder(), prefs );
				}
				
			}

			loadDigitizerInfo( rootElt );
			
			String version ="";
			Element versionElt =rootElt.getChild( "Version" );
			if ( versionElt != null ) {
				Attribute versionAttr =versionElt.getAttribute( "v" );
				if ( versionAttr != null ) {
					version =versionAttr.getValue();
				}
			}
			
			// Unzip samples when a new version is run
			if ( !"".equals( version ) && !MappingToolKitImpl.VERSION.equals( version ) ) {
				userHomeHelper.unzipSamples();
			}
		}
		
		return frames;
	}
	
	private XMLOutputter getXMLOutputter() {
        XMLOutputter outputter =new XMLOutputter();
        Format format =Format.getPrettyFormat();
        format.setEncoding("UTF-8")
        		.setOmitDeclaration(false)
        		.setOmitEncoding(false);
        outputter.setFormat( format );      
        
        return outputter;
    }
    
    private void writeXMLFile( String fileName, XMLOutputter outputter, Element rootElt ) throws IOException {
        Writer out = new BufferedWriter(new OutputStreamWriter(
                			new FileOutputStream(fileName), "UTF8"));
        out.write(outputter.outputString( rootElt ));
        out.close();        
    }	
    
    private void saveFrame( Element framesElt, String name, 
    						Rectangle rect, int zOrder,
    						boolean isIcon, boolean isMaximum, Integer splitPanePosition ) {
    	Element frameElt =new Element( "Frame" );
    	framesElt.addContent( frameElt );
    	
    	frameElt.setAttribute( "name", name );
    	frameElt.setAttribute( "x", Integer.toString( rect.x ) );
    	frameElt.setAttribute( "y", Integer.toString( rect.y ) ); 
    	frameElt.setAttribute( "width", Integer.toString( rect.width ) ); 
    	frameElt.setAttribute( "height", Integer.toString( rect.height ) );
    	frameElt.setAttribute( "z", Integer.toString( zOrder ) );
    	frameElt.setAttribute( "icon", Boolean.toString( isIcon ) ); 
    	frameElt.setAttribute( "maximum", Boolean.toString( isMaximum ) );   
    	
    	if ( splitPanePosition != null ) {
        	frameElt.setAttribute( "splitPanePosition", splitPanePosition.toString() );  
    	}
    }
	
	public void save( JFrameMain jFrameMain ) throws IOException {
    	
		Element rootElt =new Element( "TMapPreferences" );
		
		Element framesElt =new Element( "Frames" );
		rootElt.addContent( framesElt );
		
		JFrameThematicMap jFrameThematicMap =jFrameMain.jFrameThematicMap;
		
		saveFrame( framesElt, MAIN, jFrameMain.getBounds(), -1, 
									jFrameMain.getExtendedState() == JFrame.ICONIFIED, 
									jFrameMain.getExtendedState() == JFrame.MAXIMIZED_BOTH,
									jFrameThematicMap.getJSplitPane().getDividerLocation() );
		
		int zOrder =0;
		
		// To reverse frame ZOrder
		Component[] frames =jFrameThematicMap.getJDesktopPane().getComponents();
		for( Component comp : frames ) {
			JInternalFrame frame =null;
			
			if ( comp instanceof JInternalFrame ) {
				frame =(JInternalFrame)comp;
			} else {
				JInternalFrame.JDesktopIcon icon =(JInternalFrame.JDesktopIcon)comp;
				frame =icon.getInternalFrame();
			}
			
			String name ="";
			
			if ( frame instanceof JFrameMap ) {
				name =MAP;
			} else if ( frame instanceof JFrameData ) {
				name =ATTRS;
			} else if ( frame instanceof JFrameCartographicDisplay ) {
				name =CD;
			} else if ( frame instanceof JFrameAnnotations ) {
				name =ANNOTATIONS;
			} 

			if ( !name.equals("") ) {
				saveFrame( framesElt, name, frame.getBounds(), zOrder++, frame.isIcon(), frame.isMaximum(), null );
			}
		}
		
		Element versionElt =new Element( "Version" );
		rootElt.addContent( versionElt );
		versionElt.setAttribute( "v", MappingToolKitImpl.VERSION );
		
		rootElt.addContent( saveDigitizerInfo() );
		
        XMLOutputter outputter =getXMLOutputter();        	
        writeXMLFile( new UserHomeHelper().getTMapDir() + CONFIG_FILE, outputter, rootElt );
	}
	
	private Element saveDigitizerInfo() {
		DigitizerService ds =DigitizerServiceImpl.getInstance();
		
		Element dsElt =new Element( "Digitizer" );
		
		Element dcElt =new Element( "DeviceConfig" );
		dsElt.addContent( dcElt );
		dcElt.setAttribute( "port", ds.getCommunicationPort() != null ? ds.getCommunicationPort() : "" );
		dcElt.setAttribute( "baudRate", Integer.toString( ds.getBaudRate() ) );
		dcElt.setAttribute( "dataBitsConfig", Integer.toString( ds.getDataBitsConfiguration() ) );
		dcElt.setAttribute( "parityConfig", Integer.toString( ds.getParity() ) );		
		dcElt.setAttribute( "stopBits", Integer.toString( ds.getStopBits() ) );			
		
		Element dfcElt =new Element( "DataFormatConfig" );
		dsElt.addContent( dfcElt );
		dfcElt.setAttribute( "fieldSeparator", ds.getFieldSeparator() );
		dfcElt.setAttribute( "lineSeparator", ds.getLineSeparator() );
		dfcElt.setAttribute( "newPointButton", ds.getNewPointButton() );
		dfcElt.setAttribute( "closePolygonButton", ds.getClosePolygonButton() );
		
		Element rcsElt =new Element( "RelativeCoordSystem" );
		dsElt.addContent( rcsElt );
		rcsElt.setAttribute( "xMin", Double.toString( ds.getXMin() ) );
		rcsElt.setAttribute( "yMin", Double.toString( ds.getYMin() ) );
		rcsElt.setAttribute( "xMax", Double.toString( ds.getXMax() ) );
		rcsElt.setAttribute( "yMax", Double.toString( ds.getYMax() ) );		
		
		return dsElt;
	}
	
	private void loadDigitizerInfo( Element rootElt ) throws DataConversionException {
		DigitizerService ds =DigitizerServiceImpl.getInstance();

		Element dsElt =rootElt.getChild( "Digitizer" );
		
		if ( dsElt != null ) {
			Element dcElt =dsElt.getChild( "DeviceConfig" );
			
			if ( dcElt != null ) {
				ds.setCommunicationPort( dcElt.getAttributeValue( "port" ) );
				ds.setBaudRate( dcElt.getAttribute( "baudRate" ).getIntValue() );
				ds.setDataBitsConfiguration( dcElt.getAttribute( "dataBitsConfig" ).getIntValue() );
				ds.setParity( dcElt.getAttribute( "parityConfig" ).getIntValue() );		
				ds.setStopBits( dcElt.getAttribute( "stopBits" ).getIntValue() );	
			}
			
			Element dfcElt =dsElt.getChild( "DataFormatConfig" );
			if ( dfcElt != null ) {
				ds.setFieldSeparator( dfcElt.getAttributeValue( "fieldSeparator" ) );
				ds.setLineSeparator( dfcElt.getAttributeValue( "lineSeparator" ) );
				ds.setNewPointButton( dfcElt.getAttributeValue( "newPointButton" ) );
				ds.setClosePolygonButton( dfcElt.getAttributeValue( "closePolygonButton" ) );
			}
			
			Element rcsElt =dsElt.getChild( "RelativeCoordSystem" );
			if ( rcsElt != null ) {
				ds.setXMin( rcsElt.getAttribute( "xMin" ).getDoubleValue() );
				ds.setYMin( rcsElt.getAttribute( "yMin" ).getDoubleValue() );
				ds.setXMax( rcsElt.getAttribute( "xMax" ).getDoubleValue() );
				ds.setYMax( rcsElt.getAttribute( "yMax" ).getDoubleValue() );
			}
		}
	}
	
}
